package org.ctstudio.common.containers;

public class Range<T extends Comparable<? super T>> {
  private T from;
  private T to;

  public T getFrom() {
    return from;
  }

  public void setFrom(T from) {
    this.from = from;
  }

  public T getTo() {
    return to;
  }

  public void setTo(T to) {
    this.to = to;
  }

  public Range() {
  }

  public Range(T start, T end) {
    this.from = start;
    this.to = end;
  }

  @SuppressWarnings("unchecked")
  @Override
  public boolean equals(Object obj) {
    if (this.getClass().isInstance(obj)) {
      return this.equals((Range<T>) obj);
    }
    return false;
  }

  public boolean equals(Range<T> another) {
    if (this == another) {
      return true;
    } else if (another == null) {
      return false;
    }
    return this.from.equals(another.from) && this.to.equals(another.to);
  }

  public boolean contains(Range<T> another) {
    return this.from.compareTo(another.from) <= 0 && this.to.compareTo(another.to) >= 0;
  }

  public boolean startWith(Range<T> another) {
    return this.from.equals(another.from);
  }

  public boolean endWith(Range<T> another) {
    return this.to.equals(another.to);
  }

  public int compare(T a, T b) {
    if (a == null) {
      if (b == null) {
        return 0;
      } else {
        return -1;
      }
    } else if (b == null) {
      return 1;
    } else {
      return a.compareTo(b);
    }
  }

  public T max(T a, T b) {
    if (a == null) {
      return a;
    } else if (b == null) {
      return b;
    } else {
      return a.compareTo(b) > 0 ? a : b;
    }
  }

  public T min(T a, T b) {
    if (a == null) {
      return a;
    } else if (b == null) {
      return b;
    } else {
      return a.compareTo(b) < 0 ? a : b;
    }
  }

  public <T1 extends Range<T>> T1 intersect(Class<T1> c, Range<T> another) {
    if (another == null) {
      return null;
    }
    T start = max(this.from, another.from);
    T end = min(this.to, another.to);
    if (start == null || end == null) {
      return null;
    } else if (start.compareTo(end) < 0) {
      T1 result = null;
      try {
        result = c.newInstance();
      } catch (InstantiationException | IllegalAccessException e) {
        // It is impossible
      }
      result.setFrom(start);
      result.setTo(end);
      return result;
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  public Range<T> intersect(Range<T> another) {
    return this.intersect(this.getClass(), another);
  }
}
